<template>
  <el-row id="metro-container">
    <img id="train-template" class="train" src="../../assets/img/metroExper/train.png">
    <svg id="metro" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
      <g id="g-box" font-size="10"></g>
    </svg>
  </el-row>
</template>

<script>
  import MetroPainter from '../../assets/js/MetroPainter';
  import metroData from '../../assets/json/metro.json';

  export default {
    data() {
      return {
        metro: null,
      };
    },
    props: {
      /**
       * 速度, 单位是距离每秒
       */
      speed: Number,
      /**
       * 列车数量
       */
      trainNumber: Number
    },
    watch: {
      trainNumber(val) {
        if (this.metro == null) {
          return
        }
        this.metro.update()
      }
    },
    mounted() {
      this.metro = new Metro(this);
      this.metro.update()
      this.metro.frame();
    }
  };

  /**
   * 轨道交通线路
   */
  class Metro {
    constructor(vm) {
      this.vm = vm
      MetroPainter(metroData);
      this.path = document.querySelector('#metro-path');
      // 铁路总长度
      this.length = this.path.getTotalLength();
      this.trains = [];
    }

    update() {
      const oldTrainNumber = this.trains.length
      const trainNumber = this.vm.trainNumber
      if (oldTrainNumber === trainNumber) {
        return
      }
      if (oldTrainNumber > trainNumber) {
        for (let i = trainNumber; i < oldTrainNumber; i++) {
          this.trains[i].destroy()
        }
        this.trains.length = trainNumber
        return
      }
      for (let i = oldTrainNumber; i < trainNumber; i++) {
        this.appendTrain()
      }
    }

    appendTrain() {
      const distanceBetween = (this.length * 2) / this.vm.trainNumber
      let distance = 0;
      let direction = 1;
      if (this.trains.length > 0) {
        const lastTrain = this.trains[this.trains.length - 1]
        direction = lastTrain.direction
        distance = lastTrain.distance + direction * distanceBetween
        if (distance < 0) {
          distance += distanceBetween
          direction = 1
        } else if (distance > this.length) {
          distance -= distanceBetween
          direction = -1
        }
      }
      const train = new Train(this.vm, distance, direction);
      this.trains.push(train);
    }

    frame() {
      this.trains.forEach((train) => {
        train.nextFrame(this);
      });
      requestAnimationFrame(this.frame.bind(this));
    }

    getPointAtLength(length) {
      return this.path.getPointAtLength(length);
    }
  }

  /**
   * 火车
   */
  class Train {
    static EPS = 1e-5;
    // 车宽度
    static WIDTH = 25;
    // 车长度
    static LENGTH = 25;
    // 火车总数
    static Total = 0;

    /**
     * @param distance 距离铁路起点的距离, 0 表示从起点出发
     * @param direction 行驶方向, 1 表示去程, -1 表示返程
     */
    constructor(vm, distance, direction) {
      this.vm = vm
      this.node = document.getElementById('train-template').cloneNode(true);
      Train.Total++;
      this.node.id = 'train-' + Train.Total;
      document.getElementById('metro-container').appendChild(this.node);

      // 距离起点距离
      this.distance = distance;
      // 方向， 1 表示去程, -1 表示返程
      this.direction = direction;
      this.preTime = performance.now();
    }

    destroy() {
      this.node.remove()
    }

    nextFrame(metro) {
      if (this.distance >= metro.length) {
        this.direction = -1;
      } else if (this.distance < Train.EPS) {
        this.direction = 1;
      }
      const prePosition = metro.getPointAtLength(this.distance);
      const now = performance.now();
      const diff = (this.vm.speed - 50) * (now - this.preTime) / 1e3;
      this.preTime = now;
      this.distance = Math.max(Math.min(this.distance + diff * this.direction, metro.length), 0);
      const curPosition = metro.getPointAtLength(this.distance);
      const x = curPosition.x - Train.LENGTH / 2;
      const y = curPosition.y - Train.WIDTH / 2;
      const angle = Train.getRotation(prePosition, curPosition)
      let transform = ''
      if (this.direction === 1) {
        transform = 'translate3d(' + x + 'px,' + y + 'px, 1px) rotate(' + (angle - 30) + 'deg) rotateY(180deg)'
      } else {
        transform =
          'translate3d(' + x + 'px,' + y + 'px, 1px) rotate(' + (angle + 30) + 'deg) rotateY(180deg) rotateX(180deg)'
      }
      // IE
      this.node.style.transform = transform;
      // Chrome&Safari
      this.node.style.WebkitTransform = transform;
    }

    // 获取前进切线角度
    static getRotation(a, b) {
      const dy = b.y - a.y;
      let dx = b.x - a.x;
      if (Math.abs(dx) < Math.EPS) {
        dx = Math.EPS;
      }
      const k = dy / dx;
      const angle = (Math.atan(k) * 180) / Math.PI;
      return dx >= 0 ? angle : angle + 180;
    }
  }
</script>
<style lang="less" scoped="scoped">
  .train {
    width: 25px;
    height: 25px;
    position: absolute;
    z-index: 100;
    display: block;
  }

  #train-template {
    display: none;
  }

  #metro-container{
    position: absolute;
    top: 0;
    left: calc(50% - 390px);
    width: 780px;
    height: 550px;
  }

  #metro {
    position: absolute;
    top: 0;
    left: calc(50% - 390px);
    width: 780px;
    height: 550px;
    display: block;
    margin: 0 auto;
  }
</style>
